#include <system/GlutApplication.h>

namespace ne
{
    template<> GlutApplication* Singleton<GlutApplication>::mpInstance = 0;

    Engine* GlutApplication::engine = 0;

    GlutApplication::GlutApplication()
    {
        const char *argv = "NEGlutApplication";
        InitializeGlut(1, (char**)&argv);
    }

    GlutApplication::GlutApplication(int argc, char *argv[])
    {
        InitializeGlut(argc, argv);
    }

    GlutApplication::~GlutApplication()
    {
        delete engine;
        engine = 0;
    }

    bool GlutApplication::InitializeGlut(int argc, char *argv[])
    {
        atexit(GlutApplication::AtExit);
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGBA |
                            GLUT_DOUBLE |
                            GLUT_DEPTH |
                            GLUT_ACCUM |
                            GLUT_MULTISAMPLE);
        glutInitWindowSize(1280, 720);
        glutCreateWindow("NE Glut Application");
        glutReshapeFunc(GlutApplication::ResizeWindow);
        //glutDisplayFunc(redraw);
        //glutIdleFunc(redraw);
        glutMouseFunc(GlutApplication::MouseFunc);
        glutMotionFunc(GlutApplication::MouseMoveFunc);
        glutPassiveMotionFunc(GlutApplication::MouseMoveFunc);
        //glutMouseWheelFunc();

        glutKeyboardFunc(GlutApplication::KeyboardDown);
        glutKeyboardUpFunc(GlutApplication::KeyboardUp);
        glutSpecialFunc(GlutApplication::SpecialDown);
        glutSpecialUpFunc(GlutApplication::SpecialUp);

        engine = new Engine();
        if (!engine->initialize())
        {
            delete engine;
            engine = 0;
            return false;
        }
        return true;
    }

    void GlutApplication::AtExit()
    {
        delete mpInstance;
        mpInstance = 0;
    }

    void GlutApplication::ResizeWindow(int width, int height)
    {
        engine->setResolution(uint32_t(width), uint32_t(height));
    }

    void GlutApplication::MouseFunc(int button, int state, int x, int y)
    {
        if (state == GLUT_DOWN)
        {
            switch (button)
            {
                case GLUT_LEFT_BUTTON:
                    engine->setMouseButtonDown(MB_LEFT_BUTTON);
                    break;

                case GLUT_MIDDLE_BUTTON:
                    engine->setMouseButtonDown(MB_MIDDLE_BUTTON);
                    break;

                case GLUT_RIGHT_BUTTON:
                    engine->setMouseButtonDown(MB_RIGHT_BUTTON);
                    break;

                default:
                    break;
            }
        }
        else
        {
            switch (button)
            {
                case GLUT_LEFT_BUTTON:
                    engine->setMouseButtonUp(MB_LEFT_BUTTON);
                    break;

                case GLUT_MIDDLE_BUTTON:
                    engine->setMouseButtonUp(MB_MIDDLE_BUTTON);
                    break;

                case GLUT_RIGHT_BUTTON:
                    engine->setMouseButtonUp(MB_RIGHT_BUTTON);
                    break;

                default:
                    break;
            }
        }
    }

    void GlutApplication::MouseMoveFunc(int x, int y)
    {
        engine->setMousePosition(x, y);
    }

    void GlutApplication::KeyboardDown(unsigned char key, int x, int y)
    {
        printf("Key = %x(%c)\n", key, key);
        engine->setKeyboardKeyDown(Keyboard::ConvertASCIIKey(key));
        engine->setCharacter(key);
    }

    void GlutApplication::KeyboardUp(unsigned char key, int x, int y)
    {
        engine->setKeyboardKeyUp(Keyboard::ConvertASCIIKey(key));
    }

    void GlutApplication::SpecialDown(int key, int x, int y)
    {
        printf("Special Key = %x(%d)\n", key, key);
        engine->setKeyboardKeyDown(ConvertGlutSpecialKey(key));
    }

    void GlutApplication::SpecialUp(int key, int x, int y)
    {
        engine->setKeyboardKeyUp(ConvertGlutSpecialKey(key));
    }

    KeyboardKey GlutApplication::ConvertGlutSpecialKey(int key)
    {
        switch (key)
        {
            case GLUT_KEY_F1:  return KK_F1;
            case GLUT_KEY_F2:  return KK_F2;
            case GLUT_KEY_F3:  return KK_F3;
            case GLUT_KEY_F4:  return KK_F4;
            case GLUT_KEY_F5:  return KK_F5;
            case GLUT_KEY_F6:  return KK_F6;
            case GLUT_KEY_F7:  return KK_F7;
            case GLUT_KEY_F8:  return KK_F8;
            case GLUT_KEY_F9:  return KK_F9;
            case GLUT_KEY_F10: return KK_F10;
            case GLUT_KEY_F11: return KK_F11;
            case GLUT_KEY_F12: return KK_F12;

            case GLUT_KEY_LEFT:  return KK_LEFT;
            case GLUT_KEY_RIGHT: return KK_RIGHT;
            case GLUT_KEY_UP:    return KK_UP;
            case GLUT_KEY_DOWN:  return KK_DOWN;

            case GLUT_KEY_PAGE_UP:   return KK_PGUP;
            case GLUT_KEY_PAGE_DOWN: return KK_PGDOWN;
            case GLUT_KEY_HOME:      return KK_HOME;
            case GLUT_KEY_END:       return KK_END;
            case GLUT_KEY_INSERT:    return KK_INSERT;

            default: return KK_UNASSIGNED;
        }
    }

    GlutApplication& GlutApplication::GetSingleton()
    {
        assert(mpInstance);
        return *mpInstance;
    }

    GlutApplication* GlutApplication::GetSingletonPtr()
    {
        return mpInstance;
    }
}
